/************************************************************************

								Heroes IV
					  Copyright 2001, The 3DO Company

	------------------------------------------------------------------
  						 combat_ai_move_action.cpp

	$Header: $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "combat_ai_move_action.h"

#include <limits>
#include <sstream>
#include "combat_ai.h"
#include "battlefield.h"
#include "combat_ai_action_ptr.h"
#include "combat_ai_defend_action.h"
#include "debug_message.h"
#include "obstacle_type.h"
#include "stationary_combat_object.h"
#include "town_image_level.h"

void t_combat_ai_move_action::weigh_action(t_combat_ai const& owner)
{
}


void t_combat_ai_move_action::perform_action(void)
{
	perform_move(m_battlefield, m_point);
}


void t_combat_ai_move_action::perform_move( t_battlefield& battlefield, t_map_point_2d destination)
{
	// May become fancier later...
	t_combat_creature&		creature = *battlefield.get_acting_creature();

	if (creature.get_cell_position() == destination)
	{
		battlefield.defend();
		return;
	}

	// get path
	t_combat_path			path;
	t_combat_path_finder&	path_finder = creature.get_path_finder();

	if (!path_finder.get_path( destination, path ))
	{
		battlefield.defend();
		return;
	}

	int	movement = creature.get_combat_movement();

	if (path[0].move_cost <= movement)
	{
		battlefield.begin_move(destination);
		return;
	}

	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	int						minimum_move;
	int						turns;
	double					weight;
	double					best_weight;
	t_map_point_2d			best_point;
	t_combat_path_point		point;
	bool					first = true;

	// determine minimum distance to move
	turns = (path[0].move_cost + movement - 1) / movement;
	minimum_move = path[0].move_cost - (turns - 1) * movement;

	// determine point that is within 1 turn's move that has the best weight (least negative)
	// that is also >= the minimum movement
	int i;

	for (i = path.size(); i--; )
	{
		point = path[i];
		weight = data.get_influence_weight( point );
		if (point.move_cost < minimum_move)
			continue;
		if (point.blocked)
			continue;
		if (point.move_cost > movement)
			break;
		if (!first && weight < best_weight)
			continue;
		first = false;
		best_weight = weight;
		best_point = point;
	}
	if (first)
		best_point = destination;
	battlefield.begin_move( best_point );
}

// ------------------------------------------------------------------------------
// check if one side has any creatures in the castle
// ------------------------------------------------------------------------------
static bool has_creatures_in_castle( t_battlefield& battlefield, bool side )
{
	t_combat_creature_list::iterator	index = battlefield.creatures_begin();
	t_combat_creature_list::iterator	end = battlefield.creatures_end();
	t_combat_creature*					creature;

	for (; index != end; ++index)
	{
		creature = *index;
		if (creature->get_number() == 0)
			continue;
		if (creature->belongs_to_defender() != side)
			continue;
		if (battlefield.is_in_castle( creature->get_cell_position() ))
			return true;
	}
	return false;
}

// ------------------------------------------------------------------------------
// generate an action to move away from castle wall, to hide from enemies
// ------------------------------------------------------------------------------
t_combat_ai_action_ptr generate_hide_in_castle_action( t_combat_creature const& creature )
{
	t_battlefield&			battlefield = creature.get_battlefield();

	if (battlefield.get_castle_level() == k_town_image_village)
		return 0;

	if (creature.has_ability( k_ability_ranged ) || creature.has_damage_spells())
		return 0;

	if (has_creatures_in_castle( battlefield, !creature.get_controller()))
		return 0;

	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	t_combat_path_finder&	path_finder = creature.get_path_finder();
	t_map_point_2d			point;
	t_map_point_2d			closest_destination;
	int						distance;
	int						closest_distance;
	bool					destination_found = false;
	int						right_column;
	int						movement = creature.get_combat_movement();
	int						end_row = battlefield.get_castle_row() - k_wall_safe_distance
									- battlefield.get_wall_thickness()
									- creature.get_footprint_size() - 1;

	for (point.row = 0; point.row < end_row; ++point.row)
	{
		point.column = battlefield.get_row_start( point.row );
		right_column = battlefield.get_row_end( point.row );
		for (; point.column < right_column; ++point.column)
		{
			t_combat_path_data const& path_data = path_finder.get_data( point );

			if (!path_data.visited)
				continue;
			if (path_data.is_tower)
				continue;
			if (path_data.blocked)
				continue;
			distance = path_data.move_cost;
			if (distance > movement)
				continue;
			if (destination_found && distance >= closest_distance)
				continue;
			destination_found = true;
			closest_distance = distance;
			closest_destination = point;
		}
	}
	if (!destination_found)
		return 0;
	return new t_combat_ai_move_action( battlefield, closest_destination, 
										creature.get_ai_value_per_action() * 0.1 );
}

// ------------------------------------------------------------------------------
// generate an action to move into arrow towers
// ------------------------------------------------------------------------------
t_combat_ai_action_ptr generate_move_to_tower_action( t_combat_creature const& creature )
{
	t_battlefield&			battlefield = creature.get_battlefield();

	if (battlefield.get_castle_level() != k_town_image_castle)
		return 0;

	if (creature.is_in_tower())
		return 0;

	if (!creature.has_ability( k_ability_ranged ) && !creature.has_damage_spells())
		return 0;

	t_stationary_combat_object_list::iterator	index = battlefield.castle_begin();
	t_stationary_combat_object_list::iterator	end = battlefield.castle_end();
	t_stationary_combat_object const*			tower;
	t_combat_path_finder&						path_finder = creature.get_path_finder();
	t_map_point_2d								point;
	t_map_point_2d								closest;
	int											distance;
	int											closest_distance;
	bool										found = false;
	int											footprint_size;

	footprint_size = creature.get_footprint_size() / 2;
	for (; index != end; ++index)
	{
		tower = *index;
		if (tower->get_obstacle_type() != k_obstacle_castle_tower)
			continue;
		point = tower->get_footprint_center();
		point >>= k_battlefield_subcell_shift;
		point.row -= footprint_size;
		point.column -= footprint_size;
		
		t_combat_path_data const& path_data = path_finder.get_data( point );

		if (!path_data.visited)
			continue;
		if (path_data.blocked)
			continue;
		distance = path_data.move_cost;
		if (found && distance >= closest_distance)
			continue;
		found = true;
		closest_distance = distance;
		closest = point;
	}
	if (!found)
		return 0;

	double increase = (creature.get_damage_low() + creature.get_damage_high()) * 0.25;
	double value = creature.get_damage_increase_ai_value( increase );
	
	value += creature.get_defense_increase_ai_value( 0.5, true )
		   + creature.get_defense_increase_ai_value( 0.5, false );

	return new t_combat_ai_move_action( battlefield, closest, value );
}

// ------------------------------------------------------------------------------
// generate an action to move away from castle wall, to hide from enemies
// ------------------------------------------------------------------------------
t_combat_ai_action_ptr generate_move_to_wall_action( t_combat_creature const& creature )
{
	t_battlefield&			battlefield = creature.get_battlefield();

	if (battlefield.get_castle_level() == k_town_image_village)
		return 0;

	if (!creature.has_ability( k_ability_ranged ) && !creature.has_damage_spells())
		return 0;

	if (has_creatures_in_castle( battlefield, !creature.get_controller()))
		return 0;

	t_ai_combat_data_cache&	data = battlefield.get_data_cache();
	t_combat_path_finder&	path_finder = creature.get_path_finder();
	t_map_point_2d			point;
	t_map_point_2d			closest_destination;
	int						distance;
	int						closest_distance;
	bool					destination_found = false;
	int						right_column;
	int						movement = creature.get_combat_movement();
	int						end_row = battlefield.get_castle_row()
									- battlefield.get_wall_thickness()
									- creature.get_footprint_size();

	for (point.row = creature.get_cell_position().row + 1; point.row < end_row; ++point.row)
	{
		point.column = battlefield.get_row_start( point.row );
		right_column = battlefield.get_row_end( point.row );
		for (; point.column < right_column; ++point.column)
		{
			t_combat_path_data const& path_data = path_finder.get_data( point );

			if (!path_data.visited)
				continue;
			if (path_data.is_tower)
				continue;
			if (path_data.blocked)
				continue;
			distance = path_data.move_cost;
			if (destination_found)
			{
				if (point.row < closest_destination.row)
					continue;
				if (point.row == closest_destination.row
					&& distance >= closest_distance)
					continue;
			}
			destination_found = true;
			closest_distance = distance;
			closest_destination = point;
		}
	}
	if (!destination_found)
		return 0;
	return new t_combat_ai_move_action( battlefield, closest_destination, 
										creature.get_ai_value_per_action() * 0.1 );
}

// ------------------------------------------------------------------------------
// generate all move actions
// ------------------------------------------------------------------------------
void generate_move_actions( t_combat_ai& combat_ai )
{
	t_combat_creature&		acting_creature = combat_ai.get_actor();
	t_combat_ai_action_ptr	action;

	action = generate_hide_in_castle_action( acting_creature );
	if (action != 0)
		combat_ai.consider_action( action );
	action = generate_move_to_tower_action( acting_creature );
	if (action != 0)
		combat_ai.consider_action( action );
	action = generate_move_to_wall_action( acting_creature );
	if (action != 0)
		combat_ai.consider_action( action );
}
